package com.xdf.xzymanagementsystem.config;

import com.xdf.xzymanagementsystem.entity.SysPermission;
import com.xdf.xzymanagementsystem.entity.SysRole;
import com.xdf.xzymanagementsystem.entity.SysUser;
import com.xdf.xzymanagementsystem.enums.PermissionConstant;
import com.xdf.xzymanagementsystem.enums.RoleConstant;
import com.xdf.xzymanagementsystem.enums.SeparatorConstant;
import com.xdf.xzymanagementsystem.service.SysUserService;
import com.xdf.xzymanagementsystem.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @program: xzy-management-system
 * @description: 自定义shiro验证
 * @author: 张柯
 * @create: 2020-12-31 15:28
 **/
@Component
@Slf4j
public class ShiroRealm extends AuthorizingRealm {
    private SysUserService service;

    @Autowired
    public void setService(SysUserService service) {
        this.service = service;
    }

    @Override
//    public boolean supports(AuthenticationToken token) {
//        return token instanceof UsernamePasswordToken;
//    }
    // 上面是张柯的写法
    public boolean supports(AuthenticationToken token) {
        return token !=null && token instanceof MyUsernamePasswordToken;
    }

    /**
     * 授权
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Integer userId = Integer.parseInt(String.valueOf(principalCollection.getPrimaryPrincipal()));
        SysUser user = null;
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        try {
            user = service.selectByPrimaryKey(userId);
            List<SysPermission> permissionList = service.selectPermissionByUserId(user.getId());
            List<SysRole> roleList = service.selectRolesByUserId(user.getId());
            info.setRoles(getRoles(roleList));
            info.setStringPermissions(getPermissions(permissionList));
        } catch (Exception e) {
            e.printStackTrace();
        }
        //  设置权限
        return info;
    }

    /**
     * 验证信息
     * 2022年1月14日 10:59:34 chanchaw
     * 改进验证逻辑，之前只验证用户名和密码（系统内是工号和密码），此后要工号+手机号码+密码一起验证
     * @param authenticationToken shiro 的接口类型，之后要强转为自定义的 CustomTokenModel
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        MyUsernamePasswordToken reqToken = (MyUsernamePasswordToken)authenticationToken; // 强转为自定义 model
        String userLoginName = String.valueOf(authenticationToken.getPrincipal());
        String userPassword = new String((char[]) authenticationToken.getCredentials());
        String userPhoneActual = Optional.ofNullable(reqToken.getUserPhone()).orElse("");

        SysUser user = null;
        try {
            //  获取登录用户
            user = service.selectByLoginName(userLoginName);
        } catch (Exception e) {
            log.error("获取用户名失败!");
            throw new AuthenticationException("获取用户名失败!");
        }
        if (user == null) throw new AuthenticationException("用户不存在!");

        // 验证手机号码正确性
        String userPhoneMatcher = Optional.ofNullable(user.getUserPhone()).orElse("");
        if( !userPhoneActual.equalsIgnoreCase(userPhoneMatcher) )
            throw new AuthenticationException("手机号错误!");

        //  获取登录密码
        String md5pwd = MD5Util.toMD5(user.getLoginPassword());
        if (!md5pwd.equals(userPassword)) throw new AuthenticationException("密码错误！");
        if (user.getUserState() == 0) throw new AuthenticationException("用户已停用！禁止登录！");
        deleteOtherSessions(String.valueOf(user.getId()));
        return new SimpleAuthenticationInfo(String.valueOf(user.getId()), md5pwd, this.getName());
    }

    /**
     * 删除重复
     *
     * @param loginUserId
     */
    private void deleteOtherSessions(String loginUserId) {
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
        Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
        for (Session session : sessions) {
            String userName = String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
            if (userName != null && userName.equals(loginUserId)) {
                sessionManager.getSessionDAO().delete(session);
            }
        }
    }

    /**
     * 转换角色
     *
     * @param roles
     * @return
     */
    private Set<String> getRoles(List<SysRole> roles) {
        Set<String> set = new HashSet<>();
        if (CollectionUtils.isEmpty(roles)) return set;
        roles.stream().forEach(item -> {
            set.add(item.getRoleName());
        });
        if (roles.stream().filter(item -> item.getRoleAdminSign() != null && item.getRoleAdminSign()).count() > 0) {
            set.add(RoleConstant.ADMIN_ROLE);
        } else if (roles.stream().filter(x -> x.getRoleWorkshopOnly() != null && x.getRoleWorkshopOnly()).count() > 0) {
            set.add(RoleConstant.WORKSHOP_ROLE);
        }
        return set;
    }

    /**
     * 获取权限吗
     *
     * @param list
     * @return
     */
    public static Set<String> getPermissions(List<SysPermission> list) {
        Set<String> set = new HashSet<>();
        if (CollectionUtils.isEmpty(list)) return set;
        list.stream().forEach(item -> {
            if (Optional.ofNullable(item).map(x -> x.getSysShow()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.SHOW.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysAdd()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.ADD.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysModify()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.MODIFY.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysAudit()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.AUDIT.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysDelete()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.DELETE.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysExport()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.EXPORT.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysPrint()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.PRINT.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysPrice()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.PRICE.name());
            }
            if (Optional.ofNullable(item).map(x -> x.getSysMoney()).orElse(0) == 1) {
                set.add(item.getSysMenuId() + SeparatorConstant.SEPARATOR + PermissionConstant.MONEY.name());
            }
        });
        return set;
    }
}